/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package base.server;

import base.message.Message;
import base.node.Daemon;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import networking.SocketMessageFactory;
import statistics.DatabaseConnection;

/**
 * Handles the transactions through the broker 
 * @author artem
 */
public class ServingThread extends Thread {

    private ServerSocket serverSocket;
    private Socket socket;
    protected ObjectInputStream ois;
    protected ObjectOutputStream oos;
    private Object obj;
    private boolean connected;
    private int nodeID;
    private int portOffset;
    private DatabaseConnection db;

    public ServingThread(int portOffset, int nodeID) {
        try {
            this.nodeID = nodeID;
            this.portOffset = portOffset;
//			db = new DatabaseConnection();
            serverSocket = new ServerSocket(portOffset + nodeID);
            serverSocket.setReuseAddress(true);
            serverSocket.setReceiveBufferSize(1);
        } catch (IOException ex) {
            Logger.getLogger(ServingThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void run() {
        try {
            socket = serverSocket.accept();
            connected = true;
            oos = new ObjectOutputStream(socket.getOutputStream());
            oos.flush();
            ois = new ObjectInputStream(socket.getInputStream());
            while (connected) {
                obj = ois.readObject();
                if (obj instanceof Message) {
                    Message msg = (Message) obj;
                    Broker.print("Message received from node " + nodeID + " with id " + msg.getTransmitterInfo().getMessageId());

                    Daemon daemon = new Daemon(nodeID);
                    // Searching for neighbours
                    Vector<Integer> receivers = daemon.findNodesInRange();
                    // Forward the message
                    for (int i = 0; i < receivers.size(); i++) {
                        Broker.getServingThread(receivers.get(i).intValue() - 1).send(msg);
                    }
                } else if (obj instanceof SocketMessageFactory) {
                    SocketMessageFactory smf = (SocketMessageFactory) obj;
                    if (smf.getMsgType().equals("CONNECTION_CLOSE")) {
                        connected = false;
                    }
                }
            }
        } catch (ClassNotFoundException cnfe) {
            Logger.getLogger(ServingThread.class.getName()).log(Level.SEVERE, null, cnfe);
        } catch (IOException ioe) {
            Logger.getLogger(ServingThread.class.getName()).log(Level.SEVERE, null, ioe);
        } finally {
            try {
                socket.close();
                serverSocket.close();
            } catch (IOException ioe) {
                Logger.getLogger(ServingThread.class.getName()).log(Level.SEVERE, null, ioe);
            }
        }
    }

    @Override
    public void interrupt() {
        SocketMessageFactory smf = new SocketMessageFactory();
        smf.setMsgType("CONNECTION_CLOSE");
        send(smf);
    }

    /**
     * Sends a message
     * @param msg The message to be sent
     */
    public void send(Object msg) {
        try {
            oos.writeObject(msg);
//			db.recordEntry(message);
        } catch (IOException ioe) {
            Logger.getLogger(ServingThread.class.getName()).log(Level.SEVERE, null, ioe);
//            Broker.print("Error sending messages from " + message.getNodeInfo().getId());
        }
    }
    /**
     * Replaces the tempMsg with a new one, without broadcast references
     * @param tempMsg The initial tempMsg, with the broadcast references
     * @param index The index of the broadcast reference
     * @return The modified tempMsg
     */
//	private Message getBrodcastMessage(Message message, int index) {
//		ArrayList<Integer> neighbours = MovementArea.getNeighboursList(message.getData().getNodeInfo());
//		RouteNodeList rnaNeighbours = new RouteNodeList(neighbours.size());
//		for(int i:neighbours){
//			rnaNeighbours.add(new RouteNodeAbstraction(i, message.getTargetNodes().get(index).isTarget(), message.getTargetNodes().get(index).getForwardNodeList()));
//		}
//		message.getTargetNodes().remove(index);
//		RouteNodeList tmp = message.getTargetNodes();
//		if (tmp.addAll(rnaNeighbours)){
//			message.setTargetNodes(tmp);
//		}
//		return message;
//	}
    /**
     * Checks if a tempMsg should be broadcasted or not
     * @param tempMsg
     * @return
     */
//	private boolean checkBroadcast(Message message, int index) {
//		if((message.getTargetNodes().size()==1)&&(message.getTargetNodes().get(index).getNodeId()==-1)){
//			return true;
//		} else {
//			return false;
//		}
//	}
    /**
     * Checks whether a tempMsg can be sent
     * @param msg The tempMsg to be sent
     * @param index The index of the specific target node within the ArrayList of target nodes of the tempMsg
     * @return true if the tempMsg can be sent to the target node
     */
//	private boolean checkSendConditions(Message tempMsg, int index) {
//		boolean ret = false;
//		ret = MovementArea.isWithinRadius(tempMsg.getData().getNodeInfo(), tempMsg.getTargetNodes().get(index).getNodeId()) &&
//				!Broker.nodeActive[tempMsg.getTargetNodes().get(index).getNodeId()];
//		return ret;
//	}
    /**
     * Sends a list of messages to their targets
     * @param tempMsg The initial tempMsg to be parsed
     * @return true, if the sending succeeded
     */
//	private boolean sendMessages(Message tempMsg) {
//		boolean ret = false;
//		ArrayList<RouteNodeAbstraction> targetList = tempMsg.getTargetNodes();
//		for (int i = 0; i < targetList.size(); i++) {
//			Message msg = new Message(getIndexedTargetRouteNodeList(tempMsg, i), tempMsg.getMessageType(), tempMsg.getMessageSize(), tempMsg.getData(), tempMsg.getHopCount(), tempMsg.getMessageId());
//			if (checkSendConditions(msg, 0)) {
//				Broker.nodeActive[tempMsg.getTargetNodes().get(i).getNodeId()] = true;
//				Broker.send(msg);
//				Broker.nodeActive[tempMsg.getTargetNodes().get(i).getNodeId()] = false;
//			} else {
//				Broker.print("Refused connection from " + tempMsg.getData().getNodeInfo().getId() + " to " + targetList.get(i).getNodeId() + " : OUT OF RADIUS");
//			}
//		}
//		return ret;
//	}
    /**
     * Returns a RouteNodeList containing only one RouteNodeAbstraction according to the index passed
     * @param msg The tempMsg to be parsed
     * @param index The index of the RouteNodeAbstraction to be extracted
     * @return The desired RouteNodeList
     */
//	private RouteNodeList getIndexedTargetRouteNodeList(Message msg, int index) {
//		RouteNodeList ret = new RouteNodeList(1);
//		ret.add(msg.getTargetNodes().get(index));
//		return ret;
//	}
}
